/*
 * Verify the Attestation Document.
 *
 * Copyright (c) Huawei Technologies Co., Ltd. 2022. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */
#include <cbor.h>
#include <string.h>
#include <sys/time.h>
#include <openssl/pem.h>
#include <openssl/ecdsa.h>
#include "../include/qtsm_lib.h"

/* Time conversion */
#define SEC_TO_MS               1000
#define MICROS_TO_MILLIS        1000

typedef struct cbor_params {
    enum sig_algo sig_algo_id;
    EVP_PKEY *sig_pubkey;
} cbor_params;

static int check_parse_moduleid(const struct cbor_pair cur_map_pair,
                                attestation_document *cur_doc)
{
    if (cur_doc == NULL)
        return INTERNAL_ERROR;

    if (cbor_typeof(cur_map_pair.key) != CBOR_TYPE_STRING ||
        cbor_typeof(cur_map_pair.value) != CBOR_TYPE_STRING)
        return CBOR_TYPE_ERROR;

    if (cbor_string_is_indefinite(cur_map_pair.key) ||
        cbor_string_is_indefinite(cur_map_pair.value))
        return CBOR_DATA_INDEFINITE;

    if (cbor_string_handle(cur_map_pair.key) == NULL ||
        cbor_string_length(cur_map_pair.key) != strlen("module_id") ||
        strncmp(cbor_string_handle(cur_map_pair.key), "module_id",
            strlen("module_id")) != 0 ||
        cbor_string_length(cur_map_pair.value) <= 0 ||
        cbor_string_length(cur_map_pair.value) >= QTSM_MODULE_ID_MAX_SIZE)
        return CBOR_CONTENT_ERROR;

    memcpy(cur_doc->doc.module_id, cbor_string_handle(cur_map_pair.value),
        cbor_string_length(cur_map_pair.value));

    return NO_ERROR;
}

static int check_parse_digest(const struct cbor_pair cur_map_pair,
                              const struct cbor_params *condition,
                              attestation_document *cur_doc)
{
    unsigned char *val_str = NULL;

    if (condition == NULL || cur_doc == NULL)
        return INTERNAL_ERROR;

    if (cbor_typeof(cur_map_pair.key) != CBOR_TYPE_STRING ||
        cbor_typeof(cur_map_pair.value) != CBOR_TYPE_STRING)
        return CBOR_TYPE_ERROR;

    if (cbor_string_is_indefinite(cur_map_pair.key) ||
        cbor_string_is_indefinite(cur_map_pair.value))
        return CBOR_DATA_INDEFINITE;

    if (cbor_string_handle(cur_map_pair.key) == NULL ||
        cbor_string_length(cur_map_pair.key) != strlen("digest") ||
        strncmp(cbor_string_handle(cur_map_pair.key),
            "digest", strlen("digest")) != 0 ||
        cbor_string_length(cur_map_pair.value) <= 0)
        return CBOR_CONTENT_ERROR;

    val_str = cbor_string_handle(cur_map_pair.value);
    if (val_str == NULL)
        return CBOR_CONTENT_ERROR;

    switch (condition->sig_algo_id) {
        case ECDSA256:
            if (cbor_string_length(cur_map_pair.value) == strlen("SHA256") &&
                strncmp(val_str, "SHA256", strlen("SHA256")) == 0) {
                cur_doc->doc.digest = QTSM_SHA256;
                break;
            }
            return CBOR_CONTENT_ERROR;
        case ECDSA384:
            if (cbor_string_length(cur_map_pair.value) == strlen("SHA384") &&
                strncmp(val_str, "SHA384", strlen("SHA384")) == 0) {
                cur_doc->doc.digest = QTSM_SHA384;
                break;
            }
            return CBOR_CONTENT_ERROR;
        case ECDSA512:
            if (cbor_string_length(cur_map_pair.value) == strlen("SHA512") &&
                strncmp(val_str, "SHA512", strlen("SHA512")) == 0) {
                cur_doc->doc.digest = QTSM_SHA512;
                break;
            }
            return CBOR_CONTENT_ERROR;
        default:
            return CBOR_CONTENT_ERROR;
    }
    return NO_ERROR;
}

static int check_parse_timestamp(const struct cbor_pair cur_map_pair,
                                 attestation_document *cur_doc)
{
    struct timeval cur_time;
    uint64_t cur_timestamp;
    int rc;

    if (cur_doc == NULL)
        return INTERNAL_ERROR;

    if (cbor_typeof(cur_map_pair.key) != CBOR_TYPE_STRING ||
        cbor_typeof(cur_map_pair.value) != CBOR_TYPE_UINT)
        return CBOR_TYPE_ERROR;

    if (cbor_string_is_indefinite(cur_map_pair.key) ||
        cbor_int_get_width(cur_map_pair.value) != CBOR_INT_64)
        return CBOR_DATA_INDEFINITE;

    if (cbor_string_handle(cur_map_pair.key) == NULL ||
        cbor_string_length(cur_map_pair.key) != strlen("timestamp") ||
        strncmp(cbor_string_handle(cur_map_pair.key), "timestamp",
            strlen("timestamp")) != 0 ||
        cbor_get_uint64(cur_map_pair.value) <= 0)
        return CBOR_CONTENT_ERROR;

    /* Validate timestamp range */
    rc = gettimeofday(&cur_time, NULL);
    if (rc)
        return INTERNAL_ERROR;

    cur_timestamp = cur_time.tv_sec * SEC_TO_MS +
        cur_time.tv_usec / MICROS_TO_MILLIS;
    if (cur_timestamp <= cbor_get_uint64(cur_map_pair.value))
        return COBR_TIMESTAMP_ERROR;

    cur_doc->doc.timestamp = cbor_get_uint64(cur_map_pair.value);

    return NO_ERROR;
}

static int check_parse_cur_pcr(const struct cbor_pair cur_map_pair,
                               attestation_document *cur_doc, size_t idx)
{
    uint16_t cur_idx;
    size_t byte_len;

    if (cur_doc == NULL || idx >= cur_doc->doc.pcrs.pcrs_num)
        return INTERNAL_ERROR;

    if (cbor_typeof(cur_map_pair.key) != CBOR_TYPE_UINT ||
        cbor_typeof(cur_map_pair.value) != CBOR_TYPE_BYTESTRING)
        return CBOR_TYPE_ERROR;

    if (cbor_bytestring_is_indefinite(cur_map_pair.value))
        return CBOR_DATA_INDEFINITE;

    cur_idx = cbor_get_uint16(cur_map_pair.key);
    if (cur_idx >= QTSM_MAX_PCR_COUNT)
        return CBOR_CONTENT_ERROR;

    byte_len = cbor_bytestring_length(cur_map_pair.value);

    switch (cur_doc->doc.digest) {
        case QTSM_SHA256:
            if (byte_len != SHA256_LEN)
                return CBOR_CONTENT_ERROR;
            break;
        case QTSM_SHA384:
            if (byte_len != SHA384_LEN)
                return CBOR_CONTENT_ERROR;
            break;
        case QTSM_SHA512:
            if (byte_len != SHA512_LEN)
                return CBOR_CONTENT_ERROR;
            break;
        default:
            return CBOR_CONTENT_ERROR;
    }

    cur_doc->doc.pcrs.pcrs[idx].index = cur_idx;
    memcpy(cur_doc->doc.pcrs.pcrs[idx].data,
        cbor_bytestring_handle(cur_map_pair.value), byte_len);

    return NO_ERROR;
}

static int check_parse_pcrs(const struct cbor_pair cur_map_pair,
                            attestation_document *cur_doc)
{
    size_t map_size;
    size_t idx;
    int rc;

    if (cur_doc == NULL)
        return INTERNAL_ERROR;

    if (cbor_typeof(cur_map_pair.key) != CBOR_TYPE_STRING ||
        cbor_typeof(cur_map_pair.value) != CBOR_TYPE_MAP)
        return CBOR_TYPE_ERROR;

    if (cbor_string_is_indefinite(cur_map_pair.key) ||
        cbor_map_is_indefinite(cur_map_pair.value))
        return CBOR_DATA_INDEFINITE;

    map_size = cbor_map_size(cur_map_pair.value);

    if (cbor_string_handle(cur_map_pair.key) == NULL ||
        cbor_string_length(cur_map_pair.key) != strlen("pcrs") ||
        strncmp(cbor_string_handle(cur_map_pair.key),
            "pcrs", strlen("pcrs")) != 0 ||
        map_size > QTSM_MAX_PCR_COUNT)
        return CBOR_CONTENT_ERROR;

    cur_doc->doc.pcrs.pcrs_num = map_size;

    for (idx = 0; idx < map_size; idx++) {
        rc = check_parse_cur_pcr(cbor_map_handle(cur_map_pair.value)[idx],
                                 cur_doc, idx);
        if (rc != NO_ERROR)
            return CBOR_CONTENT_ERROR;
    }

    return NO_ERROR;
}

static int check_parse_cert(const struct cbor_pair cert_pair,
                            attestation_document *cur_doc)
{
    if (cur_doc == NULL)
        return INTERNAL_ERROR;

    if (cbor_typeof(cert_pair.key) != CBOR_TYPE_STRING ||
        cbor_typeof(cert_pair.value) != CBOR_TYPE_BYTESTRING)
        return CBOR_TYPE_ERROR;

    if (cbor_string_is_indefinite(cert_pair.key) ||
        cbor_bytestring_is_indefinite(cert_pair.value))
        return CBOR_DATA_INDEFINITE;

    if (cbor_string_handle(cert_pair.key) == NULL ||
        cbor_string_length(cert_pair.key) != strlen("certificate") ||
        strncmp(cbor_string_handle(cert_pair.key), "certificate",
            strlen("certificate")) != 0 ||
        cbor_bytestring_handle(cert_pair.value) == NULL ||
        cbor_bytestring_length(cert_pair.value) > QTSM_CERTIFICATE_MAX_SIZE)
        return CBOR_CONTENT_ERROR;

    cur_doc->doc.cert_len = cbor_bytestring_length(cert_pair.value);
    memcpy(cur_doc->doc.certificate, cbor_bytestring_handle(cert_pair.value),
        cbor_bytestring_length(cert_pair.value));

    return NO_ERROR;
}

static int check_parse_cabundle(const struct cbor_pair cabundle_pair,
                                attestation_document *cur_doc)
{
    size_t cabundle_array_size;
    unsigned char *cur_str = NULL;
    cbor_item_t *cabundle_item = NULL;
    size_t i;
    int rc = NO_ERROR;

    if (cur_doc == NULL)
        return INTERNAL_ERROR;

    /* Note: the format of ca bundles is different from that of AWS */
    if (cbor_typeof(cabundle_pair.key) != CBOR_TYPE_STRING ||
        cbor_typeof(cabundle_pair.value) != CBOR_TYPE_ARRAY)
        return CBOR_TYPE_ERROR;

    if (cbor_string_is_indefinite(cabundle_pair.key) ||
        cbor_array_is_indefinite(cabundle_pair.value))
        return CBOR_DATA_INDEFINITE;

    if (cbor_string_handle(cabundle_pair.key) == NULL ||
        cbor_string_length(cabundle_pair.key) != strlen("cabundle") ||
        strncmp(cbor_string_handle(cabundle_pair.key), "cabundle",
            strlen("cabundle")) != 0)
        return CBOR_CONTENT_ERROR;

    cabundle_array_size = cbor_array_size(cabundle_pair.value);
    if (cabundle_array_size > QTSM_CERTIFICATE_MAX_DEPTH)
        return CBOR_CONTENT_ERROR;

    cur_doc->doc.ca_bundles.ca_bundle_num = cabundle_array_size;
    for (i = 0; i < cabundle_array_size; i++) {
        cabundle_item = cbor_array_handle(cabundle_pair.value)[i];
        if (cabundle_item == NULL) {
            rc = CBOR_CONTENT_ERROR;
            goto error;
        }

        cur_str = cbor_bytestring_handle(cabundle_item);
        if (cur_str == NULL ||
            cbor_bytestring_length(cabundle_item) > QTSM_CERTIFICATE_MAX_SIZE) {
            rc = CBOR_CONTENT_ERROR;
            goto error;
        }

        cur_doc->doc.ca_bundles.cabundles[i].data_len =
            cbor_bytestring_length(cabundle_item);
        memcpy(cur_doc->doc.ca_bundles.cabundles[i].data, cur_str,
            cbor_bytestring_length(cabundle_item));
    }

error:
    if (cabundle_item)
        cbor_decref(&cabundle_item);
    return rc;
}

/*
 * check_required_fields() - Check if the required fields are present.
 * @cur_item (input) : The cbor-encoded attestation doc.
 * @condition (input) : The signature algorithm and so on.
 * @cur_doc (output) : The actual attestation doc.
 *
 * Context: Validation flow is:
 *         1)module_id : type : string; length : must be non-empty
 *         2)timestamp : type : string; value : sha256, sha384 or sha512
 *         3)digest : must be SHA256 or SHA384 or SHA512
 *         4)pcrs : pcrs.pcrs_num : at least 1 and at most 32
 *                  pcrs.pcrs.index : index can be in this interval [0, 32)
 *         5)certificate : must have length between 1 and 1024
 *         6)ca_bundles : must have length between 1 and 4096
 *         7)user_data_len : must have length between 1 and 512
 *         8)nonce_len : must have length between 1 and 512
 *         9)pubkey_len : must have length between 1 and 1024
 *
 * Returns:
 * * NO_ERROR on success.
 * * Others on failure.
 */
static int check_required_fields(const cbor_item_t *cur_item,
                                 const struct cbor_params *condition,
                                 attestation_document *cur_doc)
{
    uint8_t *temp_data = NULL;
    cbor_item_t *payload_item = NULL;
    struct cbor_load_result result = {0};
    struct cbor_pair *cur_map_pair = NULL;
    int rc;
    int index = 0;

    if (cur_item == NULL || condition == NULL || cur_doc == NULL)
        return INTERNAL_ERROR;

    if (cbor_typeof(cur_item) != CBOR_TYPE_BYTESTRING)
        return CBOR_TYPE_ERROR;

    if (cbor_bytestring_is_indefinite(cur_item))
        return CBOR_DATA_INDEFINITE;

    temp_data = cbor_bytestring_handle(cur_item);
    if (!temp_data)
        return INTERNAL_ERROR;

    payload_item = cbor_load(temp_data, cbor_bytestring_length(cur_item),
                             &result);
    if (result.error.code != CBOR_ERR_NONE) {
        rc = CBOR_LOAD_ERROR;
        goto error;
    }

    /* make sure map */
    if (cbor_typeof(payload_item) != CBOR_TYPE_MAP) {
        rc = CBOR_TYPE_ERROR;
        goto error;
    }

    /* All field content (moudle_id, timestamp, etc.) can not be null */
    if (cbor_map_size(payload_item) != CBOR_PAYLOAD_MAP_NUM) {
        rc = CBOR_CONTENT_ERROR;
        goto error;
    }

    /* module_id */
    rc = check_parse_moduleid(cbor_map_handle(payload_item)[index++], cur_doc);
    if (rc != NO_ERROR)
        goto error;

    /* timestamp */
    rc = check_parse_timestamp(cbor_map_handle(payload_item)[index++], cur_doc);
    if (rc != NO_ERROR)
        goto error;

    /* digest */
    rc = check_parse_digest(cbor_map_handle(payload_item)[index++],
        condition, cur_doc);
    if (rc != NO_ERROR)
        goto error;

    /* pcrs */
    rc = check_parse_pcrs(cbor_map_handle(payload_item)[index++], cur_doc);
    if (rc != NO_ERROR)
        goto error;

    /* certificate */
    rc = check_parse_cert(cbor_map_handle(payload_item)[index++], cur_doc);
    if (rc != NO_ERROR)
        goto error;

    /* ca bundle */
    rc = check_parse_cabundle(cbor_map_handle(payload_item)[index++], cur_doc);

error:
    if (payload_item)
        cbor_decref(&payload_item);
    return rc;
}

/*
 * verify_certificates_chain() - Verify the certificates.
 * @att_doc (input): The actual attestation doc.
 * @condition (output): The obtained public key for signature verification.
 *
 * Context: Verify the certificate/ca_bundles in attestation doc.
 *
 * Returns:
 * * NO_ERROR on success.
 * * Others on failure.
 */
static int verify_certificates_chain(const struct attestation_document *att_doc,
                                     struct cbor_params *condition)
{
    X509_STORE_CTX *store_ctx = NULL;
    X509_STORE *store = NULL;
    EVP_PKEY *evp_pkey = NULL;
    X509 *x509_tmp = NULL;
    const unsigned char *bytestring_tmp = NULL;
    uint32_t i;
    int rc;

    if (att_doc == NULL)
        return INTERNAL_ERROR;

    store_ctx = X509_STORE_CTX_new();
    if (store_ctx == NULL) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    store = X509_STORE_new();
    if (store == NULL) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    /* build ca chains */
    for (i = 0; i < att_doc->doc.ca_bundles.ca_bundle_num; i++) {
        bytestring_tmp = att_doc->doc.ca_bundles.cabundles[i].data;

        x509_tmp = d2i_X509(NULL, &bytestring_tmp,
                            att_doc->doc.ca_bundles.cabundles[i].data_len);
        if (x509_tmp == NULL) {
            rc = CBOR_CABUNDLES_ERROR;
            goto error;
        }

        rc = X509_STORE_add_cert(store, x509_tmp);
        if (!rc) {
            rc = CBOR_CABUNDLES_ERROR;
            goto error;
        }
        X509_free(x509_tmp);
        x509_tmp = NULL;
    }

    /* build certificate */
    bytestring_tmp = att_doc->doc.certificate;
    x509_tmp = d2i_X509(NULL, &bytestring_tmp, att_doc->doc.cert_len);
    if (x509_tmp == NULL) {
        rc = CBOR_CERT_ERROR;
        goto error;
    }

    /* parameter description :
     * 1) store_ctx for a subsequent verification operation;
     * 2) the trusted certificate store is set to store;
     * 3) the end entity certificate to be verified is set to cert;
     * 4) a set of additional certificates (which will be untrusted but
     *    may be used to build the chain)
     */
    rc = X509_STORE_CTX_init(store_ctx, store, x509_tmp, NULL);
    if (!rc) {
        rc = CBOR_CERT_ERROR;
        goto error;
    }

    /* verify */
    if (!X509_verify_cert(store_ctx)) {
        printf("%s\n", X509_verify_cert_error_string(X509_STORE_CTX_get_error(store_ctx)));
        rc = CBOR_CERT_VRY_ERROR;
        goto error;
    }

    /* get public key from certificate */
    evp_pkey = X509_get_pubkey(x509_tmp);
    if (evp_pkey == NULL) {
        rc = CBOR_CERT_PUBKEY_ERROR;
        goto error;
    }

    condition->sig_pubkey = evp_pkey;

    rc = NO_ERROR;

error:
    if (store_ctx)
        X509_STORE_CTX_free(store_ctx);
    if (store)
        X509_STORE_free(store);
    if (x509_tmp)
        X509_free(x509_tmp);

    return rc;
}

static int parse_protected_header(const cbor_item_t *cur_item,
    cbor_item_t **protected_item)
{
    uint8_t *temp_data = NULL;
    struct cbor_load_result result = {0};
    int rc;

    if (cbor_typeof(cur_item) != CBOR_TYPE_BYTESTRING)
        return CBOR_TYPE_ERROR;

    if (cbor_bytestring_is_indefinite(cur_item))
        return CBOR_DATA_INDEFINITE;

    temp_data = cbor_bytestring_handle(cur_item);
    if (!temp_data)
        return INTERNAL_ERROR;

    *protected_item = cbor_load(temp_data,
        cbor_bytestring_length(cur_item), &result);
    if (result.error.code != CBOR_ERR_NONE) {
        rc = CBOR_LOAD_ERROR;
        goto error;
    }

    /* make sure map */
    if (cbor_typeof(*protected_item) != CBOR_TYPE_MAP) {
        rc = CBOR_TYPE_ERROR;
        goto error;
    }

    if (cbor_map_size(*protected_item) != CBOR_PROTECTED_MAP_NUM) {
        rc = CBOR_CONTENT_ERROR;
        goto error;
    }

    rc = NO_ERROR;

error:
    return rc;
}

static int check_parse_protected_header(const cbor_item_t *cur_item,
    struct cbor_params *condition)
{
    uint8_t *temp_data = NULL;
    cbor_item_t *protected_item = NULL;
    struct cbor_load_result result = {0};
    struct cbor_pair *cur_map_pair = NULL;
    uint64_t header_label;
    int rc;

    if (cur_item == NULL || condition == NULL)
        return INTERNAL_ERROR;

    rc = parse_protected_header(cur_item, &protected_item);
    if (rc > 0)
        goto error;

    cur_map_pair = cbor_map_handle(protected_item);
    if (!cur_map_pair) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    if (cbor_typeof(cur_map_pair->key) != CBOR_TYPE_UINT ||
        cbor_typeof(cur_map_pair->value) != CBOR_TYPE_NEGINT) {
        rc = CBOR_TYPE_ERROR;
        goto error;
    }

    /* type: 1 */
    header_label = cbor_get_int(cur_map_pair->key);
    if (header_label != CBOR_LABEL_ALG) {
        rc = CBOR_CONTENT_ERROR;
        goto error;
    }

    /* algo: -35 (ecdsa384) */
    condition->sig_algo_id = ~cbor_get_int(cur_map_pair->value);

    /* Note: now only support ecdsa256/ecdsa384/ecdsa512 */
    if (condition->sig_algo_id != ECDSA256 &&
        condition->sig_algo_id != ECDSA384 &&
        condition->sig_algo_id != ECDSA512) {
        rc = CBOR_SIG_ALG_ERROR;
        goto error;
    }

    rc = NO_ERROR;

error:
    if (protected_item)
        cbor_decref(&protected_item);
    return rc;
}

static int check_parse_unprotected_header(const cbor_item_t *cur_item,
                                          struct cbor_params *condition)
{
    if (cur_item == NULL || condition == NULL)
        return INTERNAL_ERROR;

    if (cbor_typeof(cur_item) != CBOR_TYPE_MAP)
        return CBOR_TYPE_ERROR;

    if (cbor_bytestring_is_indefinite(cur_item))
        return CBOR_DATA_INDEFINITE;

    /* no item */
    if (cbor_map_size(cur_item) != 0)
        return CBOR_CONTENT_ERROR;

    return NO_ERROR;
}

/*
 * check_parse_att_doc() - Verify the cbor-en Attestation Document.
 * @cur_item (input): The cbor-en attestation doc.
 * @condition (input): The signature algorithm and so on.
 * @cur_doc (output): The actual attestation doc.
 *
 * Context: Validation flow is:
 *         1)Check if the required fields are present.
 *         2)Verify the certificates chain.
 *
 * Returns:
 * * NO_ERROR on success.
 * * Others on failure.
 */
static int check_parse_att_doc(const cbor_item_t *cur_item,
                               struct cbor_params *condition,
                               attestation_document *cur_doc)
{
    int rc;

    if (cur_item == NULL || condition == NULL || cur_doc == NULL)
        return INTERNAL_ERROR;

    rc = check_required_fields(cur_item, condition, cur_doc);
    if (rc != NO_ERROR)
        return rc;

    rc = verify_certificates_chain(cur_doc, condition);
    if (rc != NO_ERROR)
        return rc;

    return NO_ERROR;
}

static int check_parse_signature(const cbor_item_t *cur_item,
                                 const struct cbor_params condition)
{
    size_t len;

    if (cur_item == NULL)
        return INTERNAL_ERROR;

    if (cbor_typeof(cur_item) != CBOR_TYPE_BYTESTRING)
        return CBOR_TYPE_ERROR;

    if (cbor_bytestring_is_indefinite(cur_item))
        return CBOR_DATA_INDEFINITE;

    len = cbor_bytestring_length(cur_item);
    if ((condition.sig_algo_id == ECDSA256 && len != (SHA256_LEN << 1)) ||
        (condition.sig_algo_id == ECDSA384 && len != (SHA384_LEN << 1)) ||
        (condition.sig_algo_id == ECDSA512 && len != (SHA512_LEN << 1)))
        return CBOR_CONTENT_ERROR;

    return NO_ERROR;
}

/*
 * build_signature_message() - Build a Sig_structure
 * @protected_item (input): The protected header encoded in a bstr type.
 * @payload_item (input): The attestation doc encoded in a bstr type.
 * @signature_message (output): The well-defined byte stream.
 * @sig_message_len (output): The length of signature message.
 *
 * Context: The Sig_structure is:
 *          1)context: "Signature1"
 *          2)body_protected: protected header
 *          3)external_add: bstr (defaults to zero,
 *              which is not carried as part of the COSE object)
 *          4)payload: protected payload (attestation doc)
 *
 * Returns:
 * * NO_ERROR on success.
 * * Others on failure.
 */
static int build_signature_message(cbor_item_t *protected_item,
                                   cbor_item_t *payload_item,
                                   unsigned char **signature_message,
                                   size_t *sig_message_len)
{
    cbor_item_t *msg_root = NULL;
    cbor_item_t *context_item = NULL;
    cbor_item_t *external_add_item = NULL;
    size_t buffer_size;
    bool rst;
    int rc = NO_ERROR;
    int index = 0;

    if (protected_item == NULL || payload_item == NULL) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    msg_root = cbor_new_definite_array(CBOR_DOC_ITEM_NUM);
    if (msg_root == NULL) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    context_item = cbor_build_string("Signature1");
    if (context_item == NULL) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    rst = cbor_array_set(msg_root, index++, cbor_move(context_item));
    if (rst != true) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    rst = cbor_array_set(msg_root, index++, protected_item);
    if (rst != true) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    external_add_item = cbor_new_definite_bytestring();
    if (external_add_item == NULL) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    rst = cbor_array_set(msg_root, index++, cbor_move(external_add_item));
    if (rst != true) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    rst = cbor_array_set(msg_root, index++, payload_item);
    if (rst != true) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    *sig_message_len = cbor_serialize_alloc(msg_root,
        signature_message, &buffer_size);
    if (*sig_message_len == 0) {
        rc = INTERNAL_ERROR;
        goto error;
    }

error:
    if (context_item)
        cbor_decref(&context_item);
    if (external_add_item)
        cbor_decref(&external_add_item);
    if (msg_root)
        cbor_decref(&msg_root);
    return rc;
}

static int verify_cose_signature(const unsigned char *signature_message,
                                 size_t sig_message_len,
                                 const unsigned char *signature,
                                 size_t sig_len,
                                 const struct cbor_params condition)
{
    EC_KEY *eckey = NULL;
    BIGNUM *signature_r = NULL;
    BIGNUM *signature_s = NULL;
    int dgst_len;
    ECDSA_SIG *sig_val = NULL;
    SHA512_CTX ctx = {0};
    unsigned char *dgst = NULL;
    int rc;

    if (signature_message == NULL || sig_message_len == 0 ||
        signature == NULL || sig_len == 0)
        return INTERNAL_ERROR;

    switch (condition.sig_algo_id) {
        case ECDSA256:
            dgst_len = SHA256_LEN;
            break;
        case ECDSA384:
            dgst_len = SHA384_LEN;
            break;
        case ECDSA512:
            dgst_len = SHA512_LEN;
            break;
        default:
            return CBOR_CONTENT_ERROR;
    }

    dgst = malloc(dgst_len);
    if (dgst == NULL) {
        rc = INTERNAL_ERROR;
        goto error;
    }
    memset(dgst, 0, dgst_len);

    signature_r = BN_new();
    if (!signature_r) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    signature_s = BN_new();
    if (!signature_s) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    sig_val = ECDSA_SIG_new();
    if (!sig_val) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    eckey = EVP_PKEY_get1_EC_KEY(condition.sig_pubkey);
    if (!eckey) {
        rc = CBOR_CERT_PUBKEY_ERROR;
        goto error;
    }

    BN_bin2bn(signature, sig_len >> 1, signature_r);

    BN_bin2bn(signature + (sig_len >> 1), sig_len >> 1, signature_s);

    rc = ECDSA_SIG_set0(sig_val, signature_r, signature_s);
    if (!rc) {
        rc = CBOR_SIG_ERROR;
        goto error;
    }

    rc = SHA384_Init(&ctx);
    if (!rc) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    rc = SHA384_Update(&ctx, signature_message, sig_message_len);
    if (!rc) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    rc = SHA384_Final(dgst, &ctx);
    if (!rc) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    rc = ECDSA_do_verify(dgst, dgst_len, sig_val, eckey);
    if (rc == -1) {
        printf("cbor signature error!\n");
        rc = CBOR_SIG_ERROR;
    } else if (rc == 0) {
        printf("cobr incorrect signature!\n");
        rc = CBOR_SIG_ERROR;
    } else {
        rc = NO_ERROR;
    }

error:
    if (dgst)
        free(dgst);
    if (signature_r)
        BN_clear_free(signature_r);
    if (signature_s)
        BN_clear_free(signature_s);
    if (sig_val)
        free(sig_val);
    if (eckey)
        EC_KEY_free(eckey);

    return rc;
}

/*
 * verify_parse_cboren_doc() - Verify and parse the cbor-encoded attestation doc.
 * @att_doc (input): The cbor-encoded attestation doc.
 * @cur_doc (output): The cbor-decoded attestation doc.
 *
 * Context: COSE_Sign1 structure is
 *          protected parameters : Header
 *          unprotected parameters : Header
 *          payload : Attestation Document
 *          signature : Signature
 *
 * Returns:
 * * NO_ERROR on success.
 * * Others on failure.
 */
int verify_parse_cboren_doc(const uint8_t *doc_cose, const uint32_t doc_len,
                            attestation_document *cur_doc)
{
    cbor_item_t *doc_root_item = NULL;
    cbor_item_t *hdr_prot_item = NULL;
    cbor_item_t *hdr_unprot_item = NULL;
    cbor_item_t *payload_item = NULL;
    cbor_item_t *sign_item = NULL;
    struct cbor_load_result result = {0};
    struct cbor_params condition = {0};
    uint64_t tag_value;
    unsigned char *signature_message = NULL;
    size_t sig_message_len = 0;
    int rc;
    int index = 0;

    if (doc_cose == NULL || doc_len <= 0)
        return CBOR_INVALID_PARAM;

    doc_root_item = cbor_load(doc_cose, doc_len, &result);
    if (result.error.code != CBOR_ERR_NONE) {
        printf("result.error.code is %d\n", result.error.code);
        rc = CBOR_LOAD_ERROR;
        goto err;
    }

    /* Todo: Tag is array or cose_sign1 (untagged or tagged),
     * both are parsed according to COSE_Sign1 structure.
     */
    tag_value = cbor_tag_value(doc_root_item);
    if (tag_value != CBOR_COSE_SIGN1_TAG &&
        tag_value != CBOR_ARRAY_TAG)
        return CBOR_TAG_ERROR;

    /* The type, tag etc. */
    if (cbor_typeof(doc_root_item) != CBOR_TYPE_ARRAY ||
        !cbor_array_is_definite(doc_root_item)) {
        rc = CBOR_TYPE_ERROR;
        goto err;
    }

    if (cbor_array_size(doc_root_item) != CBOR_DOC_ITEM_NUM) {
        rc = CBOR_CONTENT_ERROR;
        goto err;
    }

    /* The protected parameters */
    hdr_prot_item = cbor_array_handle(doc_root_item)[index++];
    rc = check_parse_protected_header(hdr_prot_item, &condition);
    if (rc != NO_ERROR)
        goto err;

    /* The unprotected parameters */
    hdr_unprot_item = cbor_array_handle(doc_root_item)[index++];
    rc = check_parse_unprotected_header(hdr_unprot_item, &condition);
    if (rc != NO_ERROR)
        goto err;

    /* The payload */
    payload_item = cbor_array_handle(doc_root_item)[index++];
    rc = check_parse_att_doc(payload_item, &condition, cur_doc);
    if (rc != NO_ERROR)
        goto err;

    /* The signature */
    sign_item = cbor_array_handle(doc_root_item)[index++];
    rc = check_parse_signature(sign_item, condition);
    if (rc != NO_ERROR)
        goto err;

    /* verify signature */
    rc = build_signature_message(hdr_prot_item, payload_item,
                                 &signature_message, &sig_message_len);
    if (rc != NO_ERROR)
        goto err;

    rc = verify_cose_signature(signature_message, sig_message_len,
                               cbor_bytestring_handle(sign_item),
                               cbor_bytestring_length(sign_item),
                               condition);

err:
    if (doc_root_item)
        cbor_decref(&doc_root_item);
    if (sign_item)
        cbor_decref(&sign_item);
    if (payload_item)
        cbor_decref(&payload_item);
    if (hdr_unprot_item)
        cbor_decref(&hdr_unprot_item);
    if (hdr_prot_item)
        cbor_decref(&hdr_prot_item);
    if (condition.sig_pubkey)
        EVP_PKEY_free(condition.sig_pubkey);
    if (signature_message)
        free(signature_message);
    return rc;
}
